package com.ruoyi.web.controller.ma;



import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;

import com.ruoyi.common.wxUtils.WechatPayConfig;
import com.ruoyi.common.wxUtils.WechatPayRequest;
import com.ruoyi.common.wxUtils.WechatPayUrlEnum;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.util.Base64Utils;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.Signature;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;


/**
 * @Author:
 * @Description:
 **/
@Api(tags = "支付接口(API3)")
@RestController
@RequestMapping("/test")
public class DownOrderController {

    @Resource
    private WechatPayConfig wechatPayConfig;

    @Resource
    private WechatPayRequest wechatPayRequest;

    /**
     * 无需应答签名
     */
    @Resource
    private CloseableHttpClient wxPayNoSignClient;

    /**
     * type：h5、jsapi、app、native、sub_jsapi
     * @param WxPayInfo
     * @return
     */
    @ApiOperation(value = "统一下单-统一接口", notes = "统一下单-统一接口")
    @ApiOperationSupport(order = 10)
    @GetMapping("/transactions")
    public Map<String,Object> transactions(WxPayInfo wxPayInfo) {
        System.out.println("统一下单API，支付方式：{}"+wxPayInfo.getType());

        // 统一参数封装
        Map<String, Object> params = new HashMap<>(8);
        params.put("appid", wechatPayConfig.getAppId());
        params.put("mchid", wechatPayConfig.getMchId());
        params.put("description", wxPayInfo.getDesc());
        int outTradeNo = new Random().nextInt(999999999);
        params.put("out_trade_no", wxPayInfo.getOrderId());
        params.put("notify_url", wechatPayConfig.getNotifyUrl());

        Map<String, Object> amountMap = new HashMap<>(4);
        // 金额单位为分
        amountMap.put("total", changeY2F(wxPayInfo.getPrice()));
        amountMap.put("currency", "CNY");
        params.put("amount", amountMap);

        // 场景信息
        Map<String, Object> sceneInfoMap = new HashMap<>(4);
        // 客户端IP
        sceneInfoMap.put("payer_client_ip", "127.0.0.1");
        // 商户端设备号（门店号或收银设备ID）
        sceneInfoMap.put("device_id", "127.0.0.1");

        // 除H5与JSAPI有特殊参数外，其他的支付方式都一样
        if (wxPayInfo.getType().equals(WechatPayUrlEnum.H5.getType())) {

            Map<String, Object> h5InfoMap = new HashMap<>(4);
            // 场景类型:iOS, Android, Wap
            h5InfoMap.put("type", "IOS");
            sceneInfoMap.put("h5_info", h5InfoMap);
        } else if (wxPayInfo.getType().equals(WechatPayUrlEnum.JSAPI.getType()) || wxPayInfo.getType().equals(WechatPayUrlEnum.SUB_JSAPI.getType())) {
            Map<String, Object> payerMap = new HashMap<>(4);
            payerMap.put("openid", wxPayInfo.getOpenId());
            params.put("payer", payerMap);
        }

        params.put("scene_info", sceneInfoMap);

        String paramsStr = JSON.toJSONString(params);
        System.out.println("请求参数 ===> {}" + paramsStr);

        // 重写type值，因为小程序会多一个下划线(sub_type)
        String[] split = wxPayInfo.getType().split("_");
        String  newType = split[split.length - 1];

        String resStr = wechatPayRequest.wechatHttpPost(wechatPayConfig.getBaseUrl().concat(WechatPayUrlEnum.PAY_TRANSACTIONS.getType().concat(newType)), paramsStr);
        Map<String, Object> resMap = JSONObject.parseObject(resStr, new TypeReference<Map<String, Object>>(){});

        Map<String, Object> signMap = paySignMsg(resMap, wxPayInfo.getType());
        resMap.put("type",wxPayInfo.getType());
        resMap.put("signMap",signMap);
        return resMap;
    }


    private  Map<String, Object>  paySignMsg(Map<String, Object> map,String type){
        // 设置签名信息,Native与H5不需要
        if(type.equals(WechatPayUrlEnum.H5.getType()) || type.equals(WechatPayUrlEnum.NATIVE.getType()) ){
            return null;
        }

        Date date = new Date();
        String appId = wechatPayConfig.getAppId();
        String timeStamp = String.valueOf(date.getTime()/1000L);
        String nonceStr = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
        String prepayId = map.get("prepay_id").toString();
        String packageStr = "prepay_id="+prepayId;

        // 公共参数
        Map<String, Object> resMap = new HashMap<>();
        resMap.put("nonceStr",nonceStr);
        resMap.put("timeStamp",timeStamp);

        // JSAPI、SUB_JSAPI(小程序)
        if(type.equals(WechatPayUrlEnum.JSAPI.getType()) || type.equals(WechatPayUrlEnum.SUB_JSAPI.getType()) ) {
            resMap.put("appId",appId);
            resMap.put("package", packageStr);

            // 使用字段appId、timeStamp、nonceStr、package进行签名
            String paySign = createSign(resMap);
            resMap.put("signType", "SHA256withRSA");
            resMap.put("paySign", paySign);
        }
        // APP
        if(type.equals(WechatPayUrlEnum.APP.getType())) {
            resMap.put("appid",appId);
            resMap.put("prepayid", prepayId);
            // 使用字段appId、timeStamp、nonceStr、prepayId进行签名
            String sign = createSign(resMap);
            resMap.put("package", "Sign=WXPay");
            resMap.put("partnerid", wechatPayConfig.getMchId());
            resMap.put("sign", sign);
            resMap.put("signType", "HMAC-SHA256");
        }
        return resMap;
    }

    /**
     * 获取加密数据
     */
    private  String createSign(Map<String, Object> params){

        try {
            Map<String, Object> treeMap = new TreeMap<>(params);
            List<String> signList = new ArrayList<>(5);
            for (Map.Entry<String, Object> entry : treeMap.entrySet())
            {
                signList.add(entry.getKey() + "=" + entry.getValue());
            }
            String signStr = String.join("&", signList);
            String signatureStr = Stream.of(treeMap.get("appId").toString(), String.valueOf(treeMap.get("timeStamp")).toString(), treeMap.get("nonceStr").toString(),treeMap.get("package").toString())
                    .collect(Collectors.joining("\n", "", "\n"));


            signStr = signStr+"&key="+wechatPayConfig.getApiV3Key();

            Signature sign = Signature.getInstance("SHA256withRSA");
            PrivateKey privateKey = wechatPayConfig.getPrivateKey(wechatPayConfig.getKeyPemPath());

            sign.initSign(privateKey);
            sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
            return Base64Utils.encodeToString(sign.sign());

        }catch (Exception e){
            throw new RuntimeException("加密失败！");
        }
    }

    public static Integer changeY2F(String amount) {
        String currency = amount.replaceAll( "\\$|\\￥|\\,", "" );  //处理包含, ￥或者$的⾦额
        int index = currency.indexOf( "." );
        int length = currency.length();
        Long amLong = 0l;
        if (index == -1) {
            amLong = Long.valueOf( currency + "00" );
        } else if (length - index >= 3) {
            amLong = Long.valueOf( (currency.substring( 0, index + 3 )).replace( ".", "" ) );
        } else if (length - index == 2) {
            amLong = Long.valueOf( (currency.substring( 0, index + 2 )).replace( ".", "" ) + 0 );
        } else {
            amLong = Long.valueOf( (currency.substring( 0, index + 1 )).replace( ".", "" ) + "00" );
        }
        return Integer.parseInt(amLong.toString());
    }
}
